/* lolev.c - implementação da camada de baixo nível que cria a noção de um
 * canal ponto-a-ponto virtual.
 *
 * ESTE CÖDIGO NÃO PODE SER ALTERADO EXCETO POR RECOMENDAÇÃO EXPRESSA DO
 * PROFESSOR.  */ 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <time.h>

#include "tcp_socket.h"
#include "lolev_env.h"
#include "lolev.h"

/* ________________________________________________ variáveis globais ___
 *
 * Basicamente é preciso manter um socket para identificação do
 * canal virtual ponto-a-ponto, o valor do mtu e alguns dados
 * para gerar estatísticas.
 */

static int mtu, endpoint;
static int msgs_sent, msgs_rcvd, bytes_sent, bytes_rcvd, bytes_lost;
static struct timeval start_time;

/* _______________________________________________ int lolev_init( void )
 *     Inicializa a biblioteca de baixo nível que será usada para criar
 *     a abstração de um canal ponto-a-ponto entre duas máquinas.
 *     O valor de retorno é o socket que será usado na comunicação.
 */
int  lolev_init( void )
{
    getenv_vars();
    if (endpoint_is_passive) {
        endpoint = passive_socket(local_port,remote_port,&remote_host);
    } else {
        endpoint = active_socket(local_port,remote_port,&remote_host);
    }
    gettimeofday(&start_time,NULL);
    mtu = lolev_env_mtu; /* específico para este caso */
    return endpoint;
}

/* _______________________________________________ int lolev_stats( void );
 *     Exibe dados sobre a utilização do canal ponto-a-ponto.
 */
void lolev_stats( void )
{
    struct timeval now, elapsed;
    gettimeofday(&now,NULL);
    elapsed.tv_usec = now.tv_usec - start_time.tv_usec;
    elapsed.tv_sec  = now.tv_sec  - start_time.tv_sec;
    if (elapsed.tv_usec<0) { /* borrow from tv_sec */
        --elapsed.tv_sec;
        elapsed.tv_usec += 1000000;
    }
    fprintf(stderr,"Tempo desde o inicio: %d.%06d s\n",
            (int)elapsed.tv_sec, (int)elapsed.tv_usec);
    fprintf(stderr,"Mensagens enviadas:  %4d (%6d bytes)\n",
            msgs_sent, bytes_sent);
    fprintf(stderr,"Mensagens recebidas: %4d (%6d bytes recebidos, %d perdidos)\n",
            msgs_rcvd, bytes_rcvd, bytes_lost);
}

/* _______________________________________________ int lolev_mtu( void );
 *     Retorna o tamanho máximo das mensagens aceitas pelo canal. 
 */
int  lolev_mtu( void )
{
    return mtu;
}

/* ______________________________ int lolev_send( void* mensagem, int tamanho );
 *     Envia a mensagem no buffer identificado pelo apontador, contendo
 *     o número de bytes indicados por tamanho, através do meio. 
 *     Como a abstração oferecida é de um canal ponto-a-ponto,
 *     note que nunca é necessário se indicar o destino da mensagem
 *     enviada, pois ele será sempre pré-definido.
 */
int  lolev_send( void* mensagem, int tamanho )
{
    unsigned int msg_size = htonl(tamanho);
    if (tamanho>mtu) {
        fprintf(stderr,"Tentativa de enviar mensagem maior que MTU (%d>%d)\n",
                tamanho, mtu);
        lolev_stats();
        exit(1);
    }
  //  fprintf(stdout,"lolev_send(\"%s\",%d)\n", (char*)mensagem,tamanho);
    if (socket_write(endpoint,&msg_size,sizeof(int))!=sizeof(int)) {
        fprintf(stderr,"lolev_send: erro no controle do link\n");
        exit(1);
    }
    if (socket_write( endpoint, mensagem, tamanho ) != tamanho) {
        fprintf(stderr,"lolev_send: erro no envio do quadro\n");
        exit(1);
    }
    msgs_sent++;
    bytes_sent+= tamanho;
    return tamanho;
}

/* ______________________________ int lolev_recv( void* mensagem, int tammax );
 *     Função semelhante à anterior, nesse caso para receber mensagens. 
 *     A semântica dada a tammax e ao valor de retorno não as mesmas da
 *     função recv da interface de sockets.
 */
int  lolev_recv( void* mensagem, int tammax )
{
    int msg_size, toread;
    char thrash[MAXMTU];
    if (tammax>mtu) {
        fprintf(stderr,"Tentativa de ler mensagem maior que MTU (%d>%d)\n",
                tammax, mtu);
        lolev_stats();
        exit(1);
    }
  //  fprintf(stdout,"lolev_recv(%d bytes)\n",tammax);
    if (socket_read( endpoint, &msg_size, sizeof(int) ) != sizeof(int) ) {
        fprintf(stderr,"lolev_recv: erro no controle do link\n");
        exit(1);
    }
    msg_size = ntohl(msg_size);
//    fprintf(stdout,"lolev_recv: recebeu header(%d)\n",msg_size);
    toread = (msg_size < tammax)? msg_size: tammax;
    if (socket_read( endpoint, mensagem, toread)!=toread) {
        fprintf(stderr,"lolev_recv: erro no controle do link\n");
        exit(1);
    }
    msgs_rcvd++;
    bytes_rcvd += toread;
    if (msg_size > toread) {
        if (socket_read(endpoint,thrash,msg_size-toread)!=(msg_size-toread)) {
            fprintf(stderr,"lolev_recv: erro no descarte do link\n");
            exit(1);
        }
        bytes_lost += msg_size-toread;
    }
    return toread;
}

/* _______________________________________________ void lolev_shutdown( void );
 *     Finaliza a utilização da biblioteca.
 */

void lolev_shutdown( void )
{
    close(endpoint);
    lolev_stats();
}


